package com.xxkj.orcfx.service;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xxkj.orcfx.model.ExcelCustomerInfoReportVo;
import com.xxkj.orcfx.model.ThirdPartyAccount;
import com.xxkj.orcfx.utils.Base64Util;
import com.xxkj.orcfx.utils.DateUtil;
import com.xxkj.orcfx.utils.FileUtil;
import com.xxkj.orcfx.utils.HttpsClientUtil;
import com.xxkj.orcfx.web.BaiduWordEnum;
import com.xxkj.orcfx.web.OcrQuery;
import com.xxkj.orcfx.web.Resp;
import com.xxkj.orcfx.web.UserQuery;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.burningwave.core.assembler.StaticComponentContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.*;


public class BaiduCoreService {
    public static final String SERVER_HOST = "https://aip.baidubce.com";
    private static Logger log = LoggerFactory.getLogger(BaiduCoreService.class);
    static OcrService ocrService = new OcrService();

    public String imageInfoToExcel(OcrQuery ocrQuery) {
        try {
            String path = ocrQuery.getOrgPath();
            String savePath = ocrQuery.getSavePath();
            if (StringUtils.isAnyEmpty(path, savePath)) {
                throw new Exception("文件路径未填写");
            }
            String ocrType = ocrQuery.getOcrType();
            Map<String, Map<String, String>> resultMap = getPathImageInfo(path, ocrType);
            if (resultMap == null && resultMap.isEmpty()) {
                throw new Exception("未解析到影像数据，请确认目录" + path + "是否正确");
            }
            if ("2".equals(ocrType) || "4".equals(ocrType)) {
                doQueryExecutor(resultMap);
            }
            return imageInfoToExcel(savePath, resultMap);
        } catch (Exception ex) {
            log.error("系统异常", ex);
        }
        return "";

    }


    public String imageInfoToExcel(String savePath, Map<String, Map<String, String>> customerMap) throws Exception {
        if (customerMap == null || customerMap.isEmpty()) {
            throw new Exception("无数据");
        }

        String fileKey = "客户数据解析_" + DateUtil.toString(new Date(), DateUtil.YYYYMMDDMMHHSSSSS) + ".xlsx";
        String filePath = savePath + File.separator + fileKey;
        File file = new File(filePath);
        try {
            if (!file.exists()) {
                if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
                file.createNewFile();
            }

            ExcelWriter excelWriter = EasyExcel.write(file, ExcelCustomerInfoReportVo.class).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();
            WriteSheet writeSheet = EasyExcel.writerSheet("客户信息表").build();
            try {
                List<ExcelCustomerInfoReportVo> excelLoanReport = ExcelCustomerInfoReportVo.adapterMap(customerMap);
                excelWriter.write(excelLoanReport, writeSheet);
            } finally {
                if (excelWriter != null)
                    excelWriter.finish();
            }
            return filePath;
        } catch (Exception e) {
            log.error("生成导出到期提醒excel失败！", e.getMessage());
            throw new Exception("生成导出到期提醒excel失败！msg:" + e.getMessage());
        }
    }

    public Map<String, Map<String, String>> getPathImageInfo(String path, String ocrType) throws Exception {
//        String dataBasePath = ClassLoader.getSystemResource("database").getPath();
//        System.out.println(dataBasePath);
//        String dataBaseFile = dataBasePath + File.separator + "customerOcrInfo.txt";
//        System.out.println(dataBaseFile);
        String dataBaseFile = "customerOcrInfo.txt";
        Map<String, Map<String, String>> dataBaseCustomerMap = readDataBase(dataBaseFile);

        List<String> fileFullPathList = new ArrayList<>();
        File file = new File(path);        //获取其file对象
        getImageFilePath(file, fileFullPathList);
        if (CollectionUtils.isEmpty(fileFullPathList)) {
            log.info("目录：{} 无符合要求的影像资料");
            return null;
        }
        Map<String, Map<String, String>> customerMap = new HashMap<>();
        Set<String> noCacheSet = new HashSet<>();
        for (String fileFullPath : fileFullPathList) {
            File imageFile = new File(fileFullPath);
            String parentFileName = imageFile.getParentFile().getName();
            Map<String, String> customerInfoMap = null;
            // 读取缓存数据，如果无再去识别
            if (!noCacheSet.contains(parentFileName) && !Arrays.asList("3", "4").contains(ocrType)) {
                customerInfoMap = dataBaseCustomerMap.get(parentFileName);
                if (customerInfoMap != null) {
                    customerMap.put(parentFileName, customerInfoMap);
                    continue;
                }
            }
            noCacheSet.add(parentFileName);
            // 追加，可能有重复数据，读取按照最新数据为准
            customerInfoMap = customerMap.get(parentFileName);
            if (customerInfoMap == null) {
                customerInfoMap = new HashMap<>();
            }
            if ("1".equals("apply") && "1".equals("active")
                    && "1".equals("confirm") && "1".equals("idcard")) {
                continue;
            }
            int ocrFileType = sendMultiTextImgType(imageFile); // 0未匹配、1信用卡申请表、2信用卡激活、3申请确认函、4身份证
            switch (ocrFileType) {
                case 1:
                    customerInfoMap.put("apply", "1");
                    break;
                case 2:
                    customerInfoMap.put("active", "1");
                    break;
                case 3:
                    customerInfoMap.put("confirm", "1");
                    break;
                case 4:
                    if ("1".equals(customerInfoMap.get("idcard"))) {
                        break; // 已经存在，移除
                    }
                    Map<String, String> idCardInfoMap = sendMultiIdCardImgResult(imageFile);
                    if (idCardInfoMap != null && !idCardInfoMap.isEmpty()) {
                        customerInfoMap.put("idcard", "1");
                        customerInfoMap.put("idcardInfo", JSON.toJSONString(idCardInfoMap));
                    }
                    break;
                default:
                    // 啥也不干
                    break;
            }
            customerMap.put(parentFileName, customerInfoMap);
            dataBaseCustomerMap.put(parentFileName, customerInfoMap);
        }
        log.info("解析结果：{}", JSON.toJSONString(customerMap));
        saveDataBase(dataBaseCustomerMap);
        return customerMap;
    }

    public static void main(String[] args) throws IOException {
        String dataBasePath = ClassLoader.getSystemResource("database").getPath();
        System.out.println(dataBasePath);
        String dataBaseFile = dataBasePath + File.separator + "customerOcrInfo.txt";
        System.out.println(dataBaseFile);
//        List<String> textList = new ArrayList<>();
//        textList.add("测试数据写入");
//        textList.add("测试test数据写入");
//        textList.add("test");
//        FileUtil.writeFile(dataBaseFile, textList);
//        List<String> textList1 = new ArrayList<>();
//        textList1.add("测试追加数据写入数据写入1");
//        textList1.add("测试追加数据写入数据写入2");
//        textList1.add("测试追加数据写入数据写入3");
//        textList1.add("tewetwewefsdfaewewx,{}sdf2rdsms");
//        FileUtil.writeFile(dataBaseFile, textList1);
//        List<String> textList3 = FileUtil.readFile(dataBaseFile);
//        System.out.println(JSON.toJSONString(textList3));
        Map<String, Map<String, String>> customerMap = readDataBase(dataBaseFile);
        System.out.println(JSON.toJSONString(customerMap));
    }

    public static final String DB_SEPARATOR = "##";

    private static void saveDataBase(Map<String, Map<String, String>> customerMap) throws IOException {
//        String dataBasePath = ClassLoader.getSystemResource("database").getPath();
//        // customerOcrInfo.txt
//        String dataBaseFile = dataBasePath + File.separator + "customerOcrInfo.txt";
        String dataBaseFile = "customerOcrInfo.txt";
        File customerOcrFile = new File(dataBaseFile);
        if (!customerOcrFile.exists()) {
            customerOcrFile.createNewFile();
        }
        List<String> textList = new ArrayList<>();
        for (String customerKey : customerMap.keySet()) {
            textList.add(customerKey + DB_SEPARATOR + JSON.toJSONString(customerMap.get(customerKey)));
        }
        FileUtil.writeFile(dataBaseFile, textList);
    }

    private static Map<String, Map<String, String>> readDataBase(String dataBaseFile) throws IOException {
        Map<String, Map<String, String>> customerInfoMap = new HashMap<>();
        try {
            List<String> lineList = FileUtil.readFile(dataBaseFile);
            if (CollectionUtils.isEmpty(lineList)) {
                return customerInfoMap;
            }
            for (String line : lineList) {
                String[] lines = line.split(DB_SEPARATOR);
                if (lines.length != 2) {
                    log.warn("数据出现格式不正确，跳过读取，lineText:{}", line);
                    continue;
                }
                customerInfoMap.put(lines[0], JSON.parseObject(lines[1], Map.class));
            }
        } catch (Exception e) {
            log.error("读取文件异常", e);
            // 删除该文件？
        }

        return customerInfoMap;
    }

    /**
     * 请求图片识别接口
     * API: https://cloud.baidu.com/doc/OCR/s/akp3gfbmc
     *
     * @return
     */
    public JSONObject sendMultiIdCardImg(File file) throws Exception {
        log.info("OCR识别,开始:{}", file.getPath());

        if (file == null) {
            log.error("OCR识别失败,原因:未获取到图像信息");
            return null;
//            throw new Exception("未获取到图像信息");
        }

        ThirdPartyAccount thirdPartyAccount = getEIDAccount();
        if (thirdPartyAccount == null) {
            log.error("OCR识别失败,原因:未查到账号信息");
//            throw new Exception("未查到账号信息");
            return null;
        }
        JSONObject baiduOcrResp = null;
        try {
            String url = SERVER_HOST + "/rest/2.0/ocr/v1/multi_idcard?access_token=" + getAccessToken(thirdPartyAccount);
            byte[] imageByte = FileUtil.readFileByBytes(file);
            String imageStr = Base64Util.encode(imageByte);
            imageStr = FileUtil.resizeImageTo4M(imageStr);
            Map<String, String> param = new HashMap<>();
            param.put("image", imageStr); // 图像数据，base64编码后进行urlencode，要求base64编码和urlencode后大小不超过10M，最短边至少15px，最长边最大8192px，支持jpg/jpeg/png/bmp格式
            // 优先级：image > url > pdf_file > ofd_file ，当image字段存在时，url、pdf_file、ofd_file 字段失效
            log.info("请求OCR识别接口入参，url-[{}]，filePath-[{}]", url, file.getAbsolutePath());
            String result = HttpsClientUtil.postForm(url, param);
            result = String.valueOf(JSON.parse(result));
            log.info("请求OCR识别接口出参，-[{}]", result);
            baiduOcrResp = JSONObject.parseObject(result);
            String errorCode = baiduOcrResp.getString("error_code");
            String errorMsg = baiduOcrResp.getString("error_msg");
            if (StringUtils.isNotEmpty(errorCode) && !"0".equals(errorCode) && StringUtils.isNotEmpty(errorMsg)) {
//                if(errorMsg.contains("limit reached") || "18".equals(errorCode)) {
//                    throw new Exception("请求频繁，清稍后重试");
//                    return null;
//                }
//                throw new Exception(baiduOcrResp.getString("error_msg"));
                return null;
            }
        } catch (Exception e) {
            log.error("请求查身份证接口异常,Exception:{}", e);
            throw new Exception("前置OCR接口异常" + e.getMessage());
        }
        if (baiduOcrResp == null) {
            log.error("OCR识别失败,原因:OCR返回空");
            throw new Exception("OCR返回空");
        }
        return baiduOcrResp;
    }


    /**
     * 通用文字识别（标准版）
     * API: https://cloud.baidu.com/doc/OCR/s/zk3h7xz52
     *
     * @return
     */
    public JSONObject sendMultiTextImg(File file) throws Exception {
        log.info("OCR识别,开始:{}", file.getPath());
        if (file == null) {
            log.error("OCR识别失败,原因:未获取到图像信息");
            throw new Exception("未获取到图像信息");
        }

        ThirdPartyAccount thirdPartyAccount = getEIDAccount();
        if (thirdPartyAccount == null) {
            log.error("OCR识别失败,原因:未查到账号信息");
            throw new Exception("未查到账号信息");
        }
        JSONObject baiduOcrResp = null;
        Map<String, String> paramsLog = new HashMap<>();
        try {
            String url = SERVER_HOST + "/rest/2.0/ocr/v1/general_basic?access_token=" + getAccessToken(thirdPartyAccount);
            byte[] imageByte = FileUtil.readFileByBytes(file);
            String imageStr = Base64Util.encode(imageByte);
            imageStr = FileUtil.resizeImageTo4M(imageStr);
            Map<String, String> param = new HashMap<>();
            param.put("image", imageStr); // 图像数据，base64编码后进行urlencode，要求base64编码和urlencode后大小不超过10M，最短边至少15px，最长边最大8192px，支持jpg/jpeg/png/bmp格式
            // 优先级：image > url > pdf_file > ofd_file ，当image字段存在时，url、pdf_file、ofd_file 字段失效
            log.info("请求OCR识别接口入参，url-[{}]，filePath-[{}]", url, file.getAbsolutePath());
            String result = HttpsClientUtil.postForm(url, param);
            result = String.valueOf(JSON.parse(result));
            log.info("请求OCR识别接口出参，-[{}]", result);
            baiduOcrResp = JSONObject.parseObject(result);
            String errorCode = baiduOcrResp.getString("error_code");
            String errorMsg = baiduOcrResp.getString("error_msg");
            if (StringUtils.isNotEmpty(errorCode) && !"0".equals(errorCode) && StringUtils.isNotEmpty(errorMsg)) {
//                if(errorMsg.contains("limit reached") || "18".equals(errorCode)) {
//                    log.error("请求频繁，清稍后重试,errorMsg:{}", errorMsg);
//                }
//                throw new Exception(baiduOcrResp.getString("error_msg"));
                baiduOcrResp = null;
            }
        } catch (Exception e) {
            log.error("请求查身份证接口异常,Exception:{}", e);
            baiduOcrResp = null;
        }
        if (baiduOcrResp == null) {
            log.error("OCR识别失败,原因:OCR返回空");
            baiduOcrResp = null;
        }
        return baiduOcrResp;
    }

    private static String TOKE_PARAM = "?grant_type=client_credentials&client_id=%s&client_secret=%s";

    /**
     * 鉴权认证机制，获取token
     * https://ai.baidu.com/ai-doc/REFERENCE/Ck3dwjhhu
     *
     * @return
     */
    private String getAccessToken(ThirdPartyAccount thirdPartyAccount) throws Exception {
        Map<String, Object> tokenParam = new HashMap<>();
        String urlParams = String.format(TOKE_PARAM, thirdPartyAccount.getSdkSecretId(), thirdPartyAccount.getSdkSecret());
        String url = SERVER_HOST + "/oauth/2.0/token" + urlParams;
        try {
            log.info("获取百度token入参：url:{}，param:{}", url, JSON.toJSONString(tokenParam));
            String result = HttpsClientUtil.post(url, JSON.toJSONString(tokenParam));
            log.info("获取百度token返回result:{}", result);
            if (StringUtils.isEmpty(result)) {
                throw new Exception("前置OCR接口异常：获取token返回为空");
            }
            JSONObject jsonObject = JSON.parseObject(result);
            String accessToken = jsonObject.getString("access_token");
            if (StringUtils.isEmpty(accessToken)) {
                throw new Exception("前置OCR接口异常：未获取到token值");
            }
            return accessToken;
        } catch (Exception e) {
            throw new Exception("前置OCR接口异常：未获取到token值");
        }
    }

    /**
     * 百度账号配置
     *
     * @return
     */
    public ThirdPartyAccount getEIDAccount() {
        ThirdPartyAccount account = new ThirdPartyAccount();
        account.setSdkAppId("86940812");
        account.setSdkSecretId("QAII9WZI9Lm8ldt5VAl6QuvA");
        account.setSdkSecret("rAsr7HtfxNWozCTY22wEyRd0CRcVN0zQ");
        return account;
    }

    private static final String APPLY_TABLE_TEXT = "信用卡申请表";
    private static final String ACTIVE_TABLE_TEXT = "信用卡激活";
    private static final String APPLY_CONFIRM_TEXT = "申请确认函";

    private static final String[] ID_CARD_TEXT = new String[]{"居民身份证", "签发机关", "公民身份号码", "信用卡中心"};

    private int sendMultiTextImgType(File file) throws Exception {
        JSONObject res = sendMultiTextImg(file);
        if (res == null) {
            return 0;
        }
        JSONArray jsonArray = res.getJSONArray("words_result");
        int ocrFileType = 0; // 0未匹配、1信用卡申请表、2信用卡激活、3申请确认函、4身份证
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject wordsObj = jsonArray.getJSONObject(i);
            String words = wordsObj.getString("words");
            if (words.contains(APPLY_TABLE_TEXT)) {
                ocrFileType = 1;
                break;
            }
            if (words.contains(ACTIVE_TABLE_TEXT)) {
                ocrFileType = 2;
                break;
            }
            if (words.contains(APPLY_CONFIRM_TEXT)) {
                ocrFileType = 3;
                break;
            }
            // 身份证有些识别出了水印，只能当证件处理，进行一次ocr证件看否则真正身份证
            for (String idcardKey : ID_CARD_TEXT) {
                if (words.contains(idcardKey)) {
                    ocrFileType = 4;
                    break;
                }
            }
        }
        return ocrFileType;
    }

    private Map<String, String> sendMultiIdCardImgResult(File file) throws Exception {
        JSONObject res = sendMultiIdCardImg(file);
        if (res == null) {
            return null;
        }
        Map<String, String> resultData = null;
        Map<String, String> baiduWordMap = BaiduWordEnum.getEnumMap();
        JSONArray wordsArray = res.getJSONArray("words_result");
        for (int i = 0; i < wordsArray.size(); i++) {
            JSONObject wordsReuslObject = wordsArray.getJSONObject(i);
            if (wordsReuslObject == null) {
                continue;
            }
            JSONObject cardInfo = wordsReuslObject.getJSONObject("card_info");
            JSONObject cardResult = wordsReuslObject.getJSONObject("card_result");
            if (cardResult == null || cardResult.keySet().isEmpty()) {
                continue;
            }
            for (String wordKey : cardResult.keySet()) {
                JSONObject wordKeyObject = cardResult.getJSONObject(wordKey);
                if (wordKeyObject == null && baiduWordMap.get(wordKey) != null) {
                    continue;
                }
                if (resultData == null) resultData = new HashMap<>();
                resultData.put(baiduWordMap.get(wordKey), wordKeyObject.getString("words"));

            }
        }
        log.info("OCR,检测通过:[{}]", JSON.toJSONString(resultData));
        return resultData;
    }


    private static void getImageFilePath(File file, List<String> fileFullPathList) {
        File[] fs = file.listFiles();
        for (File f : fs) {
            if (f.isDirectory())    //若是目录，则递归打印该目录下的文件
                getImageFilePath(f, fileFullPathList);
            if (f.isFile() && f.getParentFile().getAbsolutePath().contains("影像件")) {//若是文件，拿影像件
                String fileType = f.getName().substring(f.getName().lastIndexOf('.') + 1);
                if (Arrays.asList("gif", "jpg", "jpeg", "png", "tif", "tiff", "bmp", "ico", "cur").contains(fileType.toLowerCase())) {
                    fileFullPathList.add(f.getAbsolutePath());
                }
            }

        }
    }


    private void doQueryExecutor(Map<String, Map<String, String>> resultMap) {
        for (Map<String, String> reportMap : resultMap.values()) {
            String idcardInfo = reportMap.get("idcardInfo");
            String customerName = null;
            String certId = null;
            if (StringUtils.isNotEmpty(idcardInfo)) {
                JSONObject idcardObj = JSON.parseObject(idcardInfo);
                customerName = idcardObj.getString(BaiduWordEnum.name.getToWord());
                certId = idcardObj.getString(BaiduWordEnum.ident.getToWord());
            }
            if (StringUtils.isAnyEmpty(customerName, certId)) {
                continue;
            }
            UserQuery userQuery = new UserQuery();
            userQuery.setName(customerName);
            userQuery.setIdCardNo(certId);
            Resp<String> queryRsp = ocrService.doQuery(userQuery);
            if (queryRsp != null && "00".equals(queryRsp.getCode())) {
                String resultRsp = queryRsp.getResult();
                reportMap.put("historyInfo", resultRsp == null ? "" : resultRsp);
            }
        }
    }
}
